home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pico / os_os2.c < prev    next >
C/C++ Source or Header  |  1996-03-16  |  31KB  |  1,569 lines

  1. /*
  2.  * $Id: os_os2.c,v 4.3 1996/03/16 21:52:46 mikes Exp $
  3.  *
  4.  * Program:    Operating system dependent routines - OS/2 Text mode
  5.  *
  6.  *
  7.  * Michael Seibel
  8.  * Networks and Distributed Computing
  9.  * Computing and Communications
  10.  * University of Washington
  11.  * Administration Builiding, AG-44
  12.  * Seattle, Washington, 98195, USA
  13.  * Internet: mikes@cac.washington.edu
  14.  *
  15.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  16.  *
  17.  *
  18.  * Pine and Pico are registered trademarks of the University of Washington.
  19.  * No commercial use of these trademarks may be made without prior written
  20.  * permission of the University of Washington.
  21.  * 
  22.  * Pine, Pico, and Pilot software and its included text are Copyright
  23.  * 1989-1996 by the University of Washington.
  24.  * 
  25.  * The full text of our legal notices is contained in the file called
  26.  * CPYRIGHT, included with this distribution.
  27.  *
  28.  *
  29.  * Notes:
  30.  *      - mouse support added (mss, 921215)
  31.  *
  32.  *  Portions of this code derived from MicroEMACS 3.10:
  33.  *
  34.  *    OS2.C:    Operating specific I/O and Spawning functions
  35.  *            under the OS/2 operating system
  36.  *            for MicroEMACS 3.10
  37.  *            (C)opyright 1988 by Daniel M. Lawrence
  38.  *
  39.  */
  40.  
  41. #include     <stdio.h>
  42. #include    <errno.h>
  43. #include    <signal.h>
  44. #include    <setjmp.h>
  45. #include    <time.h>
  46. #include    <fcntl.h>
  47. #include    <io.h>
  48.  
  49. #include    "osdep.h"
  50. #include    "estruct.h"
  51. #include    "pico.h"
  52. #include    "edef.h"
  53.  
  54.  
  55. /*
  56.  * Internal functions...
  57.  */
  58. int   checkmouse();
  59. void  invert_label(int, MENUITEM *);
  60. int   enhanced_keybrd(void);
  61. int   dont_interrupt(void);
  62. int   interrupt_ok(void);
  63. int   kbseq(int *);
  64. int   specialkey(unsigned int);
  65. void  do_alarm_signal(void);
  66. void  do_hup_signal(int sig);
  67. char *pfnexpand(char *, int);
  68. int   ssleep(long);
  69. void  mouseon();
  70. void  mouseoff();
  71.  
  72. void turnmouseoff();
  73.  
  74. /*
  75.  * Useful global def's
  76.  */
  77.  
  78. int timeout =0;
  79. static    int oldbut;            /* Previous state of mouse buttons */
  80. static  int enhncd;                       /* Enhanced keyboard */
  81. static  KBDINFO initialKbdInfo;
  82.  
  83. #ifdef    MOUSE
  84. /*
  85.  * Useful definitions...
  86.  */
  87. static int mexist = 0;        /* is the mouse driver installed? */
  88. static int nbuttons;        /* number of buttons on the mouse */
  89. static unsigned mnoop;
  90. static unsigned char okinfname[32] = {
  91.       0,    0,             /* ^@ - ^G, ^H - ^O  */
  92.       0,    0,            /* ^P - ^W, ^X - ^_  */
  93.       0,    0x17,        /* SP - ' ,  ( - /   */
  94.       0xff, 0xe0,        /*  0 - 7 ,  8 - ?   */
  95.       0x7f, 0xff,        /*  @ - G ,  H - O   */
  96.       0xff, 0xe9,        /*  P - W ,  X - _   */
  97.       0x7f, 0xff,        /*  ` - g ,  h - o   */
  98.       0xff, 0xf6,        /*  p - w ,  x - DEL */
  99.       0,    0,             /*  > DEL   */
  100.       0,    0,            /*  > DEL   */
  101.       0,    0,             /*  > DEL   */
  102.       0,    0,             /*  > DEL   */
  103.       0,    0             /*  > DEL   */
  104. };
  105. #endif
  106.  
  107.  
  108. static void set_kbd(int state)
  109. {
  110.   KBDINFO kbdInfo = initialKbdInfo;
  111.   if (state)
  112.   {
  113.     kbdInfo.fsMask &= ~(0x0001|0x0008|0x0100); /* echo,cooked */
  114.     kbdInfo.fsMask |= (0x0002|0x0004|0x0100);  /* noecho,raw,shift-rpt on */
  115.   }
  116.   KbdSetStatus(&kbdInfo, 0);
  117. }
  118.  
  119. /*
  120.  * DISable ctrl-break interruption
  121.  */
  122. dont_interrupt()
  123. {
  124.   signal (SIGINT, SIG_IGN);
  125.   signal (SIGBREAK, SIG_IGN);
  126.   set_kbd(1);
  127. }
  128.  
  129. /*
  130.  * re-enable ctrl-break interruption
  131.  */
  132. interrupt_ok()
  133. {
  134.   set_kbd(0);
  135.   signal (SIGINT, SIG_DFL);
  136.   signal (SIGBREAK, SIG_DFL);
  137. }
  138.  
  139.  
  140. /*
  141.  * return true if an enhanced keyboard is present
  142.  */
  143. enhanced_keybrd()
  144. {
  145.   return(1);
  146. }
  147.  
  148.  
  149. /*
  150.  * This function is called once to set up the terminal device streams.
  151.  */
  152. ttopen()
  153. {
  154.   initialKbdInfo.cb = sizeof(initialKbdInfo);
  155.   KbdGetStatus(&initialKbdInfo, 0);
  156.   dont_interrupt();            /* don't allow interrupt */
  157.   enhncd = enhanced_keybrd();        /* check for extra keys */
  158. #if    MOUSE
  159.   init_mouse();
  160. #else    /* !MOUSE */
  161.   mexist = 0;
  162. #endif    /* MOUSE */
  163.   return(1);
  164. }
  165.  
  166. #ifdef    MOUSE
  167. HMOU Mouse_Handle =(HMOU)-1;
  168. #ifdef FASTVIO
  169. int mouse_state =0;
  170. #endif
  171.  
  172. /* 
  173.  * init_mouse - check for and initialize mouse driver...
  174.  */
  175. init_mouse()
  176. {
  177.   if (Mouse_Handle==(HMOU)-1) {
  178.     int rc = MouOpen(NULL, &Mouse_Handle);
  179.     mexist=0;
  180.     if (rc)
  181.       return(FALSE);
  182.     mexist=1;
  183.     nbuttons=2;
  184.     MouFlushQue(Mouse_Handle);
  185.     turnmouseoff();
  186.   }
  187.   return(TRUE);
  188. }
  189.  
  190. static int mouchk()
  191. {
  192.   if (mexist)
  193.     return (Mouse_Handle==(HMOU)-1) ? init_mouse() : TRUE;
  194.   return FALSE;
  195. }
  196.  
  197. deinit_mouse()
  198. {
  199.   turnmouseoff();
  200. }
  201.  
  202.  
  203. /*
  204.  * mouseon - call made available for programs calling pico to turn ON the
  205.  *           mouse cursor.
  206.  */
  207. void
  208. mouseon()
  209. {
  210.   /* Show Cursor */
  211.   if (mouchk() && !mouse_state) {
  212. #ifdef FASTVIO
  213.     mouse_state=TRUE;
  214.     vidUpdate();
  215. #else
  216.     MouDrawPtr(Mouse_Handle);
  217. #endif
  218.   }
  219. }
  220.  
  221. void
  222. turnmouseoff()
  223. {
  224.   static NOPTRRECT r = { 0, 0, (USHORT)-1, (USHORT)-1 };
  225.   if (r.cRow == (USHORT)-1) {
  226.     VIOMODEINFO mi;
  227.     mi.cb = sizeof mi;
  228.     VioGetMode(&mi, 0);
  229.     r.cRow = mi.row-1;
  230.     r.cCol = mi.col-1;
  231.   }
  232.   /* Hide Cursor */
  233.   MouRemovePtr(&r, Mouse_Handle);
  234.   mouse_state=FALSE;
  235. }
  236.  
  237. /*
  238.  * mouseon - call made available for programs calling pico to turn OFF the
  239.  *           mouse cursor.
  240.  */
  241. void
  242. mouseoff()
  243. {
  244. #ifdef FASTVIO
  245.   /* This is ignored if in FASTVIO mode */
  246. #else
  247.   if (mouchk()) {
  248.     turnmouseoff();
  249.   }
  250. #endif
  251. }
  252. #endif
  253.  
  254.  
  255. /*
  256.  * This function gets called just before we go back home to the command
  257.  * interpreter.
  258.  */
  259. ttclose()
  260. {
  261.   if(!Pmaster)
  262.     interrupt_ok();
  263. #ifdef MOUSE
  264.   deinit_mouse();
  265. #endif
  266.   return(1);
  267. }
  268.  
  269.  
  270. /*
  271.  * Flush terminal buffer. Does real work where the terminal output is buffered
  272.  * up. A no-operation on systems where byte at a time terminal I/O is done.
  273.  */
  274. ttflush()
  275. {
  276.   return(1);
  277. }
  278.  
  279.  
  280. /*
  281.  * specialkey - return special key definition
  282.  */
  283. specialkey(kc)
  284. unsigned  kc;
  285. {
  286.   switch(kc){
  287.   case 0x3b00 : return(F1);
  288.   case 0x3c00 : return(F2);
  289.   case 0x3d00 : return(F3);
  290.   case 0x3e00 : return(F4);
  291.   case 0x3f00 : return(F5);
  292.   case 0x4000 : return(F6);
  293.   case 0x4100 : return(F7);
  294.   case 0x4200 : return(F8);
  295.   case 0x4300 : return(F9);
  296.   case 0x4400 : return(F10);
  297.   case 0x8500 : return(F11);
  298.   case 0x8600 : return(F12);
  299.   case 0x4800 : return(K_PAD_UP);
  300.   case 0x5000 : return(K_PAD_DOWN);
  301.   case 0x4b00 : return(K_PAD_LEFT);
  302.   case 0x4d00 : return(K_PAD_RIGHT);
  303.   case 0x4700 : return(K_PAD_HOME);
  304.   case 0x4f00 : return(K_PAD_END);
  305.   case 0x4900 : return(K_PAD_PREVPAGE);
  306.   case 0x5100 : return(K_PAD_NEXTPAGE);
  307.   case 0x5300 : return(K_PAD_DELETE);
  308.   case 0x48e0 : return(K_PAD_UP);            /* grey key version */
  309.   case 0x50e0 : return(K_PAD_DOWN);        /* grey key version */
  310.   case 0x4be0 : return(K_PAD_LEFT);        /* grey key version */
  311.   case 0x4de0 : return(K_PAD_RIGHT);        /* grey key version */
  312.   case 0x47e0 : return(K_PAD_HOME);        /* grey key version */
  313.   case 0x4fe0 : return(K_PAD_END);        /* grey key version */
  314.   case 0x49e0 : return(K_PAD_PREVPAGE);        /* grey key version */
  315.   case 0x51e0 : return(K_PAD_NEXTPAGE);        /* grey key version */
  316.   case 0x53e0 : return(K_PAD_DELETE);        /* grey key version */
  317.   default     : return(NODATA);
  318.   }
  319. }
  320.  
  321.  
  322. /*
  323.  * Read a character from the terminal, performing no editing and doing no echo
  324.  * at all. Also mouse events are forced into the input stream here.
  325.  */
  326. ttgetc()
  327. {
  328.   int key = kbd_getkey();
  329.   return key ? key : NODATA;
  330. }
  331.  
  332.  
  333. /*
  334.  * ctrlkey - used to check if the key hit was a control key.
  335.  */
  336. ctrlkey()
  337. {
  338.   return !!(kbd_shift() & KBDSTF_CONTROL);
  339. }
  340.  
  341.  
  342. /*
  343.  * win_multiplex - give OS/2 a shot at the CPU
  344.  */
  345. win_multiplex()
  346. {
  347.   DosSleep(32);
  348. }
  349.  
  350.  
  351. /*
  352.  * Read in a key.
  353.  * Do the standard keyboard preprocessing. Convert the keys to the internal
  354.  * character set.  Resolves escape sequences and returns no-op if global
  355.  * timeout value exceeded.
  356.  */
  357. GetKey()
  358. {
  359.   unsigned ch = 0, lch, intrupt = 0;
  360.   long timein;
  361.  
  362.   vidUpdate();
  363.   if(mexist || timeout) {
  364.     timein = time(0L);
  365. #ifdef    MOUSE
  366.     mouseon();        /* Show Cursor */
  367. #endif
  368.     while(!kbd_ready()) {
  369. #if    MOUSE
  370.       if(timeout && time(0L) >= timein+timeout){
  371.     mouseoff();        /* Hide Cursor */
  372.     return(NODATA);
  373.       }
  374.  
  375.       if(checkmouse(&ch,0,0,0)){        /* something happen ?? */
  376.     mouseoff();        /* Hide Cursor */
  377.     curwp->w_flag |= WFHARD;
  378.     return(ch);
  379.       }
  380. #else
  381.       if(time(0L) >= timein+timeout) {
  382.     mouseoff();        /* Hide Cursor */
  383.     return(NODATA);
  384.       }
  385. #endif    /* MOUSE */
  386.  
  387.       /*
  388.        * Surrender the CPU...
  389.        */
  390.       win_multiplex();
  391.     }
  392. #ifdef    MOUSE
  393.     mouseoff();        /* Hide Cursor */
  394. #endif    /* MOUSE */
  395.   }
  396.  
  397.   ch = (*term.t_getchar)();
  398.   lch = (ch&0xff);
  399.   return((lch && (lch != 0xe0 || !(ch & 0xff00))) 
  400.          ? (lch < ' ') ? (CTRL|(lch + '@')) 
  401.          : (lch == ' ' && ctrlkey()) ? (CTRL|'@') : lch
  402.          : specialkey(ch));
  403. }
  404.  
  405.  
  406. #if    MOUSE
  407. /* 
  408.  * checkmouse - look for mouse events in key menu and return 
  409.  *              appropriate value.
  410.  */
  411. int
  412. checkmouse(ch, down, xxx, yyy)
  413. unsigned *ch;
  414. int down, xxx, yyy;
  415. {
  416.   MOUQUEINFO qi;
  417.  
  418.   if(!mouchk())
  419.     return(FALSE);
  420.  
  421.   if (MouGetNumQueEl(&qi, Mouse_Handle)==0 && qi.cEvents)
  422.   {
  423.     MOUEVENTINFO m;
  424.     USHORT w = MOU_NOWAIT;
  425.  
  426.     /* check to see if any mouse buttons are different */
  427.     if (MouReadEventQue(&m, &w, Mouse_Handle)==0)
  428.     {
  429.       int k;
  430.       int rv = 0;
  431.       int button = M_BUTTON_LEFT;
  432.       int newbut = ((m.fs & 4)?1:0) | ((m.fs & 16)?2:0) | ((m.fs & 64)?4:0);
  433.  
  434.       /* only notice button changes */
  435.       if (oldbut == newbut)
  436.     return(FALSE);
  437.  
  438.       for (k=1; k != (1 << nbuttons); k <<= 1) {
  439.     /* For each button on the mouse */
  440.     if ((oldbut&k) != (newbut&k)) {
  441.       if(k == 1){
  442.         static int oindex;
  443.         int i = 0;
  444.         MENUITEM *mp;
  445.  
  446.         if(newbut&k)            /* button down */
  447.           oindex = -1;
  448.  
  449.         for(mp = mfunc; mp; mp = mp->next)
  450.           if(mp->action && M_ACTIVE(m.row, m.col, mp))
  451.         break;
  452.  
  453.         if(mp){
  454.           unsigned long r;
  455.           
  456.           r = (*mp->action)((newbut&k) ? M_EVENT_DOWN : M_EVENT_UP,
  457.                 m.row, m.col, button, 0);
  458.           if(r & 0xffff){
  459.         *ch = (unsigned)((r>>16)&0xffff);
  460.         rv  = TRUE;
  461.           }
  462.         }
  463.         else{
  464.           while(1){    /* see if we understand event */
  465.         if(i >= 12){
  466.           i = -1;
  467.           break;
  468.         }
  469.         
  470.         if(M_ACTIVE(m.row, m.col, &menuitems[i]))
  471.           break;
  472.         
  473.         i++;
  474.           }
  475.           
  476.           if(newbut&k){            /* button down */
  477.         oindex = i;            /* remember where */
  478.         if(i != -1) {            /* invert label */
  479.           invert_label(1, &menuitems[i]);
  480.         }
  481.           }
  482.           else{                /* button up */
  483.         if(oindex != -1){
  484.           if(i == oindex){
  485.             *ch = menuitems[i].val;
  486.             rv = 1;
  487.           }
  488.         }
  489.           }
  490.         }
  491.         
  492.         if(!(newbut&k) && oindex != -1) {
  493.           invert_label(0, &menuitems[oindex]);    /* restore label */
  494.         }
  495.       }
  496.       
  497.       oldbut = newbut;
  498.       return(rv);
  499.     }
  500.     ++button;
  501.       }
  502.     }
  503.   }
  504.   return(FALSE);
  505. }
  506.  
  507.  
  508. /*
  509.  * invert_label - highlight the label of the given menu item.
  510.  */
  511. void
  512. invert_label(state, m)
  513. int state;
  514. MENUITEM *m;
  515. {
  516.   USHORT r, c;
  517.   VIOCURSORINFO oldInfo, newInfo;
  518.   int i, j, p;
  519.   char *lp;
  520.   int old_state = getrevstate();
  521.  
  522.   if(m->val == mnoop)
  523.     return;
  524.  
  525.   VioGetCurPos(&r, &c, 0);
  526.   VioGetCurType(&oldInfo, 0);
  527.   newInfo = oldInfo;
  528.   newInfo.attr = (USHORT)-1;
  529.   VioSetCurType(&newInfo, 0);             /* Hide Cursor */
  530.   (*term.t_move)(m->tl.r, m->tl.c);
  531.   (*term.t_rev)(state);
  532.   for(i = m->tl.r; i <= m->br.r; i++)
  533.     for(j = m->tl.c; j <= m->br.c; j++)
  534.       if(i == m->lbl.r && j == m->lbl.c){    /* show label?? */
  535.     lp = m->label;
  536.     while(*lp && j++ < m->br.c)
  537.       (*term.t_putchar)(*lp++);
  538.     continue;
  539.       }
  540.       else (*term.t_putchar)(' ');
  541.   
  542.   (*term.t_rev)(old_state);
  543.   VioSetCurPos(r, c, 0);    /* restore old position */
  544.   VioSetCurType(&oldInfo, 0);    /* Show Cursor */
  545.   vidUpdate();
  546. }
  547. #endif    /* MOUSE */
  548.  
  549.  
  550. /*
  551.  * alt_editor - fork off an alternate editor for mail message composition
  552.  */
  553. #define MAXARGS 10
  554. alt_editor(f, n)
  555. {
  556.   char   eb[NLINE];                /* buf holding edit command */
  557.   char   *fn;                    /* tmp holder for file name */
  558.   char   *cp;
  559.   char   *args[MAXARGS];            /* ptrs into edit command */
  560.   char   *writetmp();
  561.   int    child, pid, i, done = 0;
  562.   long   l;
  563.   int    stat;
  564.   FILE   *p;
  565.  
  566.   if(Pmaster == NULL)
  567.     return(-1);
  568.  
  569.   if(gmode&MDSCUR){
  570.     emlwrite("Alternate editor not available in restricted mode", NULL);
  571.     return(-1);
  572.   }
  573.  
  574.   if(Pmaster->alt_ed == NULL){
  575.     if (!(gmode&MDADVN)) {
  576.       emlwrite("\007Unknown Command",NULL);
  577.       return(-1);
  578.     }
  579.  
  580.     if((cp=getenv("VISUAL"))!=0 || (cp=getenv("EDITOR"))!=0)
  581.       strcpy(eb, (char *)getenv("EDITOR"));
  582.     else *eb = '\0';
  583.     
  584.     while(!done) {
  585.       pid = mlreplyd("Which alternate editor ? ",eb,NLINE,QDEFLT,NULL);
  586.  
  587.       switch(pid) {
  588.       case ABORT:
  589.     return(-1);
  590.       case HELPCH:
  591.     emlwrite("no alternate editor help yet", NULL);
  592.     
  593.     /* take sleep and break out after there's help */
  594.     sleep(3);
  595.     break;
  596.       case (CTRL|'L'):
  597.     sgarbf = TRUE;
  598.       update();
  599.       break;
  600.       case TRUE:
  601.       case FALSE:            /* does editor exist ? */
  602.     if(*eb == '\0'){        /* leave silently? */
  603.       mlerase();
  604.       return(-1);
  605.     }
  606.  
  607.     done++;
  608.     break;
  609.       default:
  610.     break;
  611.       }
  612.     }
  613.   }
  614.   else
  615.     strcpy(eb, Pmaster->alt_ed);
  616.  
  617.   if((fn=writetmp(0, 1)) == NULL){        /* get temp file */
  618.     emlwrite("Problem writing temp file for alt editor", NULL);
  619.     return(-1);
  620.   }
  621.  
  622.   strcat(eb, " ");
  623.   strcat(eb, fn);
  624.  
  625.   cp = eb;
  626.   for(i=0; *cp != '\0';i++) {            /* build args array */
  627.     if(i < MAXARGS) {
  628.       args[i] = NULL;            /* in case we break out */
  629.     }
  630.     else{
  631.       emlwrite("Too many args for command!", NULL);
  632.       return(-1);
  633.     }
  634.  
  635.     while(isspace(*cp))
  636.       if(*cp != '\0')
  637.     cp++;
  638.       else break;
  639.  
  640.     args[i] = cp;
  641.     while(!isspace(*cp))
  642.       if(*cp != '\0')
  643.     cp++;
  644.       else
  645.     break;
  646.     
  647.     if(*cp != '\0')
  648.       *cp++ = '\0';
  649.   }
  650.  
  651.   args[i] = NULL;
  652.  
  653.   (*Pmaster->raw_io)(0);            /* turn OFF raw mode */
  654.  
  655.   emlwrite("Invoking alternate editor...", NULL);
  656.  
  657.   if(child=fork()) {            /* wait for the child to finish */
  658.     void (*ohup)() = signal(SIGHUP, SIG_IGN);    /* ignore signals for now */
  659.     void (*oint)() = signal(SIGINT, SIG_IGN);
  660.  
  661.     while((pid=(int)wait(&stat)) != child)
  662.       ;
  663.  
  664.     signal(SIGHUP, ohup);    /* restore signals */
  665.     signal(SIGINT, oint);
  666.   }
  667.   else {                /* spawn editor */
  668.     signal(SIGHUP, SIG_DFL);    /* let editor handle signals */
  669.     signal(SIGINT, SIG_DFL);
  670.     if(execvp(args[0], args) < 0)
  671.       exit(1);
  672.   }
  673.  
  674.   (*Pmaster->raw_io)(1);        /* turn ON raw mode */
  675.   dont_interrupt();
  676.  
  677.   /*
  678.    * replace edited text with new text 
  679.    */
  680.   curbp->b_flag &= ~BFCHG;        /* make sure old text gets blasted */
  681.   readin(fn, 0);            /* read new text overwriting old */
  682.   unlink(fn);                /* blast temp file */
  683.   curbp->b_flag |= BFCHG;        /* mark dirty for packbuf() */
  684.   ttopen();                /* reset the signals */
  685.   refresh(0, 1);            /* redraw */
  686.   return(0);
  687. }
  688.  
  689.  
  690. /*
  691.  *  bktoshell - suspend and wait to be woken up
  692.  */
  693. bktoshell()        /* suspend MicroEMACS and wait to wake up */
  694. {
  695.   int i;
  696.   static char * shell = NULL;
  697.  
  698.   if (shell == NULL) {
  699.     char *p;
  700.     shell=getenv("SHELL");
  701.     if (!shell && !(shell=getenv("COMSPEC")))
  702.       shell="CMD.EXE";
  703.     if ((p = strdup(shell)) > 0) {
  704.       for (shell = p; (p = strchr(shell, '/')) != 0; )
  705.     *p = '\\';
  706.     }
  707.   }
  708.  
  709.   (*term.t_move)(term.t_nrow, 0);
  710.   (*term.t_eeol)();
  711.   exit_text_mode(NULL);
  712.   interrupt_ok();
  713.   if (system(shell) == -1)
  714.     emlwrite("Error loading %s", shell);
  715.   else refresh(0, 1);            /* redraw */
  716.   enter_text_mode(NULL);
  717.   dont_interrupt();
  718. }
  719.  
  720.  
  721. /*
  722.  * P_open - run the given command in a sub-shell returning a file pointer
  723.  *        from which to read the output
  724.  *
  725.  * note:
  726.  *    For OS's other than unix, you will have to rewrite this function.
  727.  *    Hopefully it'll be easy to exec the command into a temporary file, 
  728.  *    and return a file pointer to that opened file or something.
  729.  */
  730. FILE *P_open(c)
  731. char *c;
  732. {
  733.   return(popen(c,"r"));
  734. }
  735.  
  736.  
  737.  
  738. /*
  739.  * P_close - close the given descriptor
  740.  *
  741.  */
  742. P_close(fp)
  743. FILE *fp;
  744. {
  745.   return(pclose(fp));
  746. }
  747.  
  748. /*
  749.  * A replacement for fflush
  750.  * relies on #define fflush os2_fflush
  751.  */
  752. #undef fflush
  753. int
  754. os2_fflush (FILE *f)
  755. {
  756.   if (f == stdout) {
  757.     vidUpdate();
  758.   }
  759.   else
  760.     fflush (f);
  761. }
  762.  
  763. /*
  764.  * ttresize - recompute the screen dimensions if necessary, and then
  765.  *          adjust pico's internal buffers accordingly
  766.  */
  767. int
  768. ttresize ()
  769. {
  770.     return (0);            /* no op */
  771. }
  772.  
  773. /*
  774.  * picosigs - Install any handlers for the signals we're interested
  775.  *          in catching.
  776.  */
  777. picosigs()
  778. {
  779.     signal(SIGHUP,  do_hup_signal);    /* deal with SIGHUP */
  780.     signal(SIGTERM, do_hup_signal);    /* deal with SIGTERM */
  781. }
  782.  
  783. /*
  784.  * do_hup_signal - jump back in the stack to where we can handle this
  785.  */
  786. void
  787. do_hup_signal(int sig)
  788. {
  789.   sig=sig;
  790.   signal(SIGHUP,  SIG_IGN);            /* ignore further SIGHUP's */
  791.   signal(SIGTERM, SIG_IGN);            /* ignore further SIGTERM's */
  792.   if(Pmaster){
  793.     extern jmp_buf finstate;
  794.  
  795.     longjmp(finstate, COMP_GOTHUP);
  796.   }
  797.   else{
  798.     /*
  799.      * if we've been interrupted and the buffer is changed,
  800.      * save it...
  801.      */
  802.     if(anycb() == TRUE){            /* time to save */
  803.       if(curbp->b_fname[0] == '\0'){    /* name it */
  804.     strcpy(curbp->b_fname, "pico.sav");
  805.       }
  806.       else{
  807.     strcat(curbp->b_fname, ".sav");
  808.       }
  809.       writeout(curbp->b_fname);
  810.     }
  811.     vttidy();
  812.     exit(1);
  813.   }
  814. }
  815.  
  816.  
  817.  
  818. #ifdef    MOUSE
  819.  
  820. /*
  821.  * end_mouse - a no-op on DOS/Windows
  822.  */
  823. void
  824. end_mouse()
  825. {
  826. }
  827.  
  828.  
  829. /*
  830.  * mouseexist - function to let outsiders know if mouse is turned on
  831.  *              or not.
  832.  */
  833. mouseexist()
  834. {
  835.     return(mexist);
  836. }
  837. #endif    /* MOUSE */
  838.  
  839.  
  840. /*
  841.  * fallowc - returns TRUE if c is allowable in filenames, FALSE otw
  842.  */
  843. fallowc(c)
  844. int c;
  845. {
  846.   return(okinfname[c>>3] & 0x80>>(c&7));
  847. }
  848.  
  849.  
  850. /*
  851.  * fexist - returns TRUE if the file exists, FALSE otherwise
  852.  */
  853. fexist(file, m, l)
  854. char *file, *m;
  855. long *l;
  856. {
  857.   struct stat    sbuf;
  858.  
  859.   if(l != NULL)
  860.     *l = 0L;
  861.  
  862.   if(stat(file, &sbuf) < 0){
  863.     if(ENOENT)                /* File not found */
  864.       return(FIOFNF);
  865.     else
  866.       return(FIOERR);
  867.   }
  868.  
  869.   if(l != NULL)
  870.     *l = sbuf.st_size;
  871.  
  872.   if(sbuf.st_mode & S_IFDIR)
  873.     return(FIODIR);
  874.  
  875.   if(m[0] == 'r')                /* read access? */
  876.     return((S_IREAD & sbuf.st_mode) ? FIOSUC : FIONRD);
  877.   else if(m[0] == 'w')            /* write access? */
  878.     return((S_IWRITE & sbuf.st_mode) ? FIOSUC : FIONWT);
  879.   else if(m[0] == 'x')            /* execute access? */
  880.     return((S_IEXEC & sbuf.st_mode) ? FIOSUC : FIONEX);
  881.   return(FIOERR);                /* what? */
  882. }
  883.  
  884.  
  885. /*
  886.  * isdir - returns true if fn is a readable directory, false otherwise
  887.  *         silent on errors (we'll let someone else notice the problem;)).
  888.  */
  889. isdir(fn, l)
  890. char *fn;
  891. long *l;
  892. {
  893.   struct stat sbuf;
  894.  
  895.   if(l)
  896.     *l = 0;
  897.  
  898.   if(stat(fn, &sbuf) < 0)
  899.     return(0);
  900.  
  901.   if(l)
  902.     *l = sbuf.st_size;
  903.  
  904.   return(sbuf.st_mode & S_IFDIR);
  905. }
  906.  
  907.  
  908. /*
  909.  * gethomedir - returns the users home directory
  910.  *              Note: home is malloc'd for life of pico
  911.  */
  912. char *gethomedir(l)
  913. int *l;
  914. {
  915.   static char *home = NULL;
  916.   static short hlen = 0;
  917.  
  918.   if(home == NULL){
  919.     char *p, buf[NLINE];
  920.  
  921.     if (home=getenv("PINEHOME"))       /* Overrides all others */
  922.       strcpy(buf, home);
  923.     else if (home=getenv("HOME"))       /* Convenient group placement */
  924.       strcpy(buf, home);
  925.     else if (home=getenv("ETC"))       /* IBM TCPIP */
  926.       strcpy(buf, home);
  927.     else sprintf(buf, "%c:\\", _getdrive());
  928.     hlen = strlen(buf);
  929.     if ((home=(char *)malloc(hlen + 1)) == NULL) {
  930.       emlwrite("Problem allocating space for home dir", NULL);
  931.       return(0);
  932.     }
  933.     strcpy(home, buf);
  934.     while ((p=strchr(home,'/')) != NULL)   /* Normalise, just in case */
  935.       *p = '\\';
  936.   }
  937.  
  938.   if(l)
  939.     *l = hlen;
  940.  
  941.   return(home);
  942. }
  943.  
  944.  
  945. /*
  946.  * homeless - returns true if given file does not reside in the current
  947.  *            user's home directory tree. 
  948.  */
  949. homeless(f)
  950. char *f;
  951. {
  952.   char *home;
  953.   int   len;
  954.  
  955.   home = gethomedir(&len);
  956.   return(strncmp(home, f, len));
  957. }
  958.  
  959.  
  960.  
  961. /*
  962.  * errstr - return system error string corresponding to given errno
  963.  *          Note: strerror() is not provided on all systems, so it's 
  964.  *          done here once and for all.
  965.  */
  966. char *errstr(err)
  967. int err;
  968. {
  969.   return((err >= 0 && err < sys_nerr) ? (char*)sys_errlist[err] : NULL);
  970. }
  971.  
  972.  
  973. /*
  974.  * getfnames - return all file names in the given directory in a single 
  975.  *             malloc'd string.  n contains the number of names
  976.  */
  977. char *getfnames(dn, pat, n, e)
  978. char *dn, *pat, *e;
  979. int  *n;
  980. {
  981.     int status;
  982.     long l;
  983.     char *names, *np, *p;
  984.     char buf[NLINE];
  985.     struct stat sbuf;
  986.     ULONG count=1;
  987.     FILEFINDBUF3 findbuf;
  988.     HDIR hdir=HDIR_CREATE;
  989.  
  990.     *n = 0;
  991.  
  992.     while ((p = strchr(dn, '/')) != NULL)
  993.       *p = '\\';
  994.  
  995.     if(stat(dn, &sbuf) < 0){
  996.     if(e)
  997.       sprintf(e, "\007Dir \"%s\": %s", dn, strerror(errno));
  998.  
  999.     return(NULL);
  1000.     } 
  1001.     else{
  1002.     l = sbuf.st_size;
  1003.     if(!(sbuf.st_mode & S_IFDIR)){
  1004.         if(e)
  1005.           sprintf(e, "\007Not a directory: \"%s\"", dn);
  1006.  
  1007.         return(NULL);
  1008.     }
  1009.     }
  1010.  
  1011.     if((names=(char *)malloc(sizeof(char)*3072)) == NULL){
  1012.     if(e)
  1013.     sprintf(e, "\007Can't malloc space for file names", NULL);
  1014.  
  1015.     return(NULL);
  1016.     }
  1017.  
  1018.     np = names;
  1019.  
  1020.     strcpy(buf, dn);
  1021.     if (*buf && buf[strlen(buf)-1] != '\\')
  1022.       strcat(buf, "\\");
  1023.     if (pat && *pat)
  1024.       strcat(buf, pat);
  1025.     if (!pat || !*pat || strchr(pat, '.')==NULL)
  1026.       strcat(buf, "*");
  1027.  
  1028.     if (DosFindFirst(buf, &hdir, FILE_NORMAL|FILE_DIRECTORY, &findbuf,
  1029.              sizeof findbuf, &count, FIL_STANDARD) != 0) {
  1030.     if(e)
  1031.       sprintf(e, "Can't find first file in \"%s\"", dn);
  1032.  
  1033.     free((char *) names);
  1034.     return(NULL);
  1035.     }
  1036.  
  1037.     do{
  1038.     (*n)++;
  1039.     p = findbuf.achName;
  1040.     while((*np++ = *p++) != '\0')
  1041.       ;
  1042.     }
  1043.     while(DosFindNext(hdir, &findbuf, sizeof findbuf, &count) == 0);
  1044.  
  1045.     return(names);
  1046. }
  1047.  
  1048.  
  1049. /*
  1050.  * fioperr - given the error number and file name, display error
  1051.  */
  1052. void
  1053. fioperr(e, f)
  1054. int  e;
  1055. char *f;
  1056. {
  1057.   switch(e){
  1058.   case FIOFNF:                /* File not found */
  1059.     emlwrite("\007File \"%s\" not found", f);
  1060.     break;
  1061.   case FIOEOF:                /* end of file */
  1062.     emlwrite("\007End of file \"%s\" reached", f);
  1063.     break;
  1064.   case FIOLNG:                /* name too long */
  1065.     emlwrite("\007File name \"%s\" too long", f);
  1066.     break;
  1067.   case FIODIR:                /* file is a directory */
  1068.     emlwrite("\007File \"%s\" is a directory", f);
  1069.     break;
  1070.   case FIONWT:
  1071.     emlwrite("\007Write permission denied: %s", f);
  1072.     break;
  1073.   case FIONRD:
  1074.     emlwrite("\007Read permission denied: %s", f);
  1075.     break;
  1076.   case FIONEX:
  1077.     emlwrite("\007Execute permission denied: %s", f);
  1078.     break;
  1079.   default:
  1080.     emlwrite("\007File I/O error: %s", f);
  1081.   }
  1082. }
  1083.  
  1084.  
  1085. /*
  1086.  * pfnexpand - pico's function to expand the given file name if there is 
  1087.  *           a leading '~'
  1088.  */
  1089. char *pfnexpand(fn, len)
  1090. char *fn;
  1091. int  len;
  1092. {
  1093.   register char *x, *y, *z;
  1094.   char *home = gethomedir(NULL);
  1095.   char name[20];
  1096.  
  1097.   if(*fn == '~') {
  1098.     for(x = fn+1, y = name; *x != '/' && *x != '\\' && *x != '\0'; *y++ = *x++);
  1099.     *y = '\0';
  1100.     if(strlen(home) + strlen(fn) > len) {
  1101.       return(NULL);
  1102.     }
  1103.     /* make room for expanded path */
  1104.     for(z=x+strlen(x),y=fn+strlen(x)+strlen(home);
  1105.         z >= x;
  1106.         *y-- = *z--);
  1107.     /* and insert the expanded address */
  1108.     for(x=fn,y=home; *y != '\0'; *x++ = *y++);
  1109.   }
  1110.  
  1111.   return(fn);
  1112. }
  1113.  
  1114. getcurdir(drv, buf)
  1115. int drv;
  1116. char *buf;
  1117. {
  1118.   LONG ml = _MAX_PATH;
  1119.   drv = drv ? toupper(drv) : _getdrive();
  1120.   buf[0] = (char)drv;
  1121.   buf[1] = ':';
  1122.   buf[2] = '\\';
  1123.   return DosQueryCurrentDir(drv-'A'+1, buf + 3, &ml) == 0;
  1124. }
  1125.  
  1126. /*
  1127.  * fixpath - make the given pathname into an absolute path
  1128.  */
  1129. fixpath(name, len)
  1130. char *name;
  1131. int  len;
  1132. {
  1133.   char *p;
  1134.   char file[_MAX_PATH];
  1135.   int  dr;
  1136.  
  1137.   if(!len)
  1138.     return(0);
  1139.  
  1140.   /* normalize: xlate any '/' into '\\' */
  1141.   while ((p=strchr(name,'/'))!=NULL)
  1142.     *p='\\';
  1143.  
  1144.   /* return the full path of given file */
  1145.   if(isalpha(name[0]) && name[1] == ':'){    /* have drive spec? */
  1146.     if(name[2] == '\\')            /* including path? */
  1147.       return 1;
  1148.     if (!getcurdir(name[0], file))
  1149.       return 0;
  1150.     name += 2;
  1151.   }
  1152.   else if(name[0] == '.' && (!name[1] || name[1] == '\\')) {
  1153.     getcurdir(0, file);
  1154.     name += (1 + name[1] == '\\');
  1155.   }
  1156.   else if(name[0] == '\\') {        /* no drive spec! */
  1157.     file[0] = (char)_getdrive();
  1158.     file[1] = ':';
  1159.     file[2] = '\0';
  1160.   }
  1161.   else getcurdir(0, file);    /* no qualification */
  1162.   
  1163.   if(*name) {                /* if name, append it */
  1164.     p = NULL;
  1165.     if (name[0] == '.' && name[1] == '.' && (!name[2] || name[2] == '\\')) {
  1166.       if ((p = strrchr(name,'\\'))!=NULL) {
  1167.         *p++ = '\0';
  1168.         if (!*p && (p=strrchr(name,'\\'))!=NULL)
  1169.           *p = '\0';
  1170.       }
  1171.       name += (2 & name[2] == '\\');
  1172.     }
  1173.     if (*name) {
  1174.       if (name[0] == '.' && (!name[1] || name[1] == '\\'))
  1175.         name += (1 + name[1] == '\\');
  1176.       if (*name) {
  1177.         if (*file && file[strlen(file)-1] != '\\' && *name != '\\')
  1178.           strcat(file, "\\");
  1179.         strcat(file, name);
  1180.       }
  1181.     }
  1182.   }
  1183.  
  1184.   strncpy(name, file, len);            /* copy back to real buffer */
  1185.   name[len-1] = '\0';                /* tie off just in case */
  1186.   return(1);
  1187. }
  1188.  
  1189.  
  1190. /*
  1191.  * compresspath - given a base path and an additional directory, collapse
  1192.  *                ".." and "." elements and return absolute path (appending
  1193.  *                base if necessary).  
  1194.  *
  1195.  *                returns  1 if OK, 
  1196.  *                         0 if there's a problem
  1197.  *                         new path, by side effect, if things went OK
  1198.  */
  1199. compresspath(base, path, len)
  1200. char *base, *path;
  1201. int  len;
  1202. {
  1203.   register int i;
  1204.   int  depth = 0;
  1205.   char *p;
  1206.   char *stack[32];
  1207.   char  pathbuf[NLINE];
  1208.  
  1209. #define PUSHD(X)  (stack[depth++] = X)
  1210. #define POPD()    ((depth > 0) ? stack[--depth] : "")
  1211.  
  1212.   strcpy(pathbuf, path);
  1213.   fixpath(pathbuf, len);
  1214.  
  1215.   p = pathbuf;
  1216.   for(i=0; pathbuf[i] != '\0'; i++){        /* pass thru path name */
  1217.     if(pathbuf[i] == C_FILESEP){
  1218.       if(p != pathbuf)
  1219.         PUSHD(p);                /* push dir entry */
  1220.       p = &pathbuf[i+1];            /* advance p */
  1221.       pathbuf[i] = '\0';            /* cap old p off */
  1222.       continue;
  1223.     }
  1224.  
  1225.     if(pathbuf[i] == '.'){            /* special cases! */
  1226.       if(pathbuf[i+1] == '.'            /* parent */
  1227.          && (pathbuf[i+2] == C_FILESEP || pathbuf[i+2] == '\0')){
  1228.         if(!strcmp(POPD(),""))        /* bad news! */
  1229.           return(0);
  1230.  
  1231.         i += 2;
  1232.         p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
  1233.       }
  1234.       else if(pathbuf[i+1] == C_FILESEP || pathbuf[i+1] == '\0'){
  1235.         i++;
  1236.         p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
  1237.       }
  1238.     }
  1239.   }
  1240.  
  1241.   if(*p != '\0')
  1242.     PUSHD(p);                    /* get last element */
  1243.  
  1244.   path[0] = '\0';
  1245.   for(i = 0; i < depth; i++){
  1246.     strcat(path, S_FILESEP);
  1247.     strcat(path, stack[i]);
  1248.   }
  1249.  
  1250.   return(1);                    /* everything's ok */
  1251. }
  1252.  
  1253.  
  1254. /*
  1255.  * tmpname - return a temporary file name in the given buffer
  1256.  */
  1257. void
  1258. tmpname(name)
  1259. char *name;
  1260. {
  1261.   char *tmpdir = getenv("TEMP");
  1262.   if (!tmpdir && (tmpdir = getenv("TMP"))==NULL)
  1263.     tmpdir="";
  1264.   sprintf(name, "%s\\%s", tmpdir, tmpnam(NULL), getpid());
  1265. }
  1266.  
  1267.  
  1268. /*
  1269.  * Take a file name, and from it
  1270.  * fabricate a buffer name. This routine knows
  1271.  * about the syntax of file names on the target system.
  1272.  * I suppose that this information could be put in
  1273.  * a better place than a line of code.
  1274.  */
  1275. void
  1276. makename(bname, fname)
  1277. char    bname[];
  1278. char    fname[];
  1279. {
  1280.   register char   *cp1;
  1281.   register char   *cp2;
  1282.  
  1283.   cp1 = &fname[0];
  1284.   while (*cp1 != 0)
  1285.     ++cp1;
  1286.  
  1287.   while (cp1!=&fname[0] && cp1[-1]!='\\')
  1288.     --cp1;
  1289.   cp2 = &bname[0];
  1290.   while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
  1291.     *cp2++ = *cp1++;
  1292.   *cp2 = 0;
  1293. }
  1294.  
  1295.  
  1296. /*
  1297.  * copy - copy contents of file 'a' into a file named 'b'.  Return error
  1298.  *        if either isn't accessible or is a directory
  1299.  */
  1300. copy(a, b)
  1301. char *a, *b;
  1302. {
  1303.   int    n, rv = 0;
  1304.   struct stat tsb, fsb;
  1305.  
  1306.   if(stat(a, &fsb) < 0){        /* get source file info */
  1307.     emlwrite("Can't Copy: %s", errstr(errno));
  1308.     return(-1);
  1309.   }
  1310.  
  1311.   if(!(fsb.st_mode&S_IREAD)){        /* can we read it? */
  1312.     emlwrite("\007Read permission denied: %s", a);
  1313.     return(-1);
  1314.   }
  1315.  
  1316.   if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
  1317.     emlwrite("\007Can't copy: %s is a directory", a);
  1318.     return(-1);
  1319.   }
  1320.  
  1321.   if(stat(b, &tsb) < 0){        /* get dest file's mode */
  1322.     switch(errno){
  1323.     case ENOENT:
  1324.       break;            /* these are OK */
  1325.     default:
  1326.       emlwrite("\007Can't Copy: %s", errstr(errno));
  1327.       return(-1);
  1328.     }
  1329.   }
  1330.   else{
  1331.     if(!(tsb.st_mode&S_IWRITE)){    /* can we write it? */
  1332.       emlwrite("\007Write permission denied: %s", b);
  1333.       return(-1);
  1334.     }
  1335.  
  1336.     if((tsb.st_mode&S_IFMT) == S_IFDIR){    /* is it directory? */
  1337.       emlwrite("\007Can't copy: %s is a directory", b);
  1338.       return(-1);
  1339.     }
  1340.  
  1341.     if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
  1342.       emlwrite("\007Identical files.  File not copied", NULL);
  1343.       return(-1);
  1344.     }
  1345.   }
  1346.  
  1347.   rv=DosCopy(a, b, DCPY_EXISTING);
  1348.  
  1349.   if(rv != 0 < 0){
  1350.     emlwrite("Copy Failed: DosCopy() error %d", rv);
  1351.     return(-1);
  1352.   }
  1353.   return(rv);
  1354. }
  1355.  
  1356.  
  1357. /*
  1358.  * Open a file for writing. Return TRUE if all is well, and FALSE on error
  1359.  * (cannot create).
  1360.  */
  1361. ffwopen(fn)
  1362. char    *fn;
  1363. {
  1364.   extern FILE *ffp;
  1365.  
  1366.   if ((ffp=fopen(fn, "w")) == NULL) {
  1367.     emlwrite("Cannot open file for writing", NULL);
  1368.     return (FIOERR);
  1369.   }
  1370.   return (FIOSUC);
  1371. }
  1372.  
  1373.  
  1374. /*
  1375.  * Close a file. Should look at the status in all systems.
  1376.  */
  1377. ffclose()
  1378. {
  1379.   extern FILE *ffp;
  1380.  
  1381.   if (fclose(ffp) != FALSE) {
  1382.     emlwrite("Error closing file", NULL);
  1383.     return(FIOERR);
  1384.   }
  1385.  
  1386.   return(FIOSUC);
  1387. }
  1388.  
  1389.  
  1390. /*
  1391.  * worthit - generic sort of test to roughly gage usefulness of using 
  1392.  *           optimized scrolling.
  1393.  *
  1394.  * note:
  1395.  *    returns the line on the screen, l, that the dot is currently on
  1396.  */
  1397. worthit(l)
  1398. int *l;
  1399. {
  1400.   int i;            /* l is current line */
  1401.   unsigned below;        /* below is avg # of ch/line under . */
  1402.  
  1403.   *l = doton(&i, &below);
  1404.   below = (i > 0) ? below/(unsigned)i : 0;
  1405.  
  1406.   return(below > 3);
  1407. }
  1408.  
  1409.  
  1410. /*
  1411.  * o_insert - optimize screen insert of char c
  1412.  */
  1413. o_insert(c)
  1414. char c;
  1415. {
  1416.   return(0);
  1417. }
  1418.  
  1419.  
  1420. /*
  1421.  * o_delete - optimized character deletion
  1422.  */
  1423. o_delete()
  1424. {
  1425.   return(0);
  1426. }
  1427.  
  1428.  
  1429. /*
  1430.  * pico_new_mail - just checks mtime and atime of mail file and notifies user 
  1431.  *               if it's possible that they have new mail.
  1432.  */
  1433. pico_new_mail()
  1434. {
  1435.   return(0);
  1436. }
  1437.  
  1438.  
  1439.  
  1440. /*
  1441.  * time_to_check - checks the current time against the last time called 
  1442.  *                 and returns true if the elapsed time is > timeout
  1443.  */
  1444. time_to_check()
  1445. {
  1446.   static time_t lasttime = 0L;
  1447.  
  1448.   if(!timeout)
  1449.     return(FALSE);
  1450.  
  1451.   if(time((long *) 0) - lasttime > (time_t)timeout){
  1452.     lasttime = time((long *) 0);
  1453.     return(TRUE);
  1454.   }
  1455.   else
  1456.     return(FALSE);
  1457. }
  1458.  
  1459.  
  1460. /*
  1461.  * sstrcasecmp - compare two pointers to strings case independently
  1462.  */
  1463. sstrcasecmp(s1, s2)
  1464. QcompType *s1, *s2;
  1465. {
  1466.   return(stricmp(*(char **)s1, *(char **)s2));
  1467. }
  1468.  
  1469.  
  1470. /*
  1471.  * sleep the given number of microseconds
  1472.  */
  1473. ssleep(s)
  1474.     clock_t s;
  1475. {
  1476.   s += clock();
  1477.   while(s > clock())
  1478.     ;
  1479. }
  1480.  
  1481.  
  1482. /*
  1483.  * chkptinit -- initialize anything we need to support composer
  1484.  *        checkpointing
  1485.  */
  1486. chkptinit(file, n)
  1487.     char *file;
  1488.     int   n;
  1489. {
  1490.     if(!file[0]){
  1491.     long gmode_save = gmode;
  1492.  
  1493.     if(gmode&MDCURDIR)
  1494.       gmode &= ~MDCURDIR;  /* so fixpath will use home dir */
  1495.  
  1496.     strcpy(file, "#picoTM0.txt");
  1497.     fixpath(file, NLINE);
  1498.     gmode = gmode_save;
  1499.     }
  1500.     else{
  1501.     int l = strlen(file);
  1502.  
  1503.     if(file[l-1] != '\\'){
  1504.         file[l++] = '\\';
  1505.         file[l]   = '\0';
  1506.     }
  1507.  
  1508.     strcpy(file + l, "#picoTM0.txt");
  1509.     }
  1510.  
  1511.     if(fexist(file, "r", NULL) == FIOSUC){ /* does file exist? */
  1512.     char copy[NLINE];
  1513.  
  1514.     strcpy(copy, "#picoTM1.txt");
  1515.     fixpath(copy, NLINE);
  1516.     rename(file, copy);  /* save so we don't overwrite it */
  1517.     }
  1518.  
  1519.     unlink(file);
  1520. }
  1521.  
  1522.  
  1523. static USHORT myShift = 0;
  1524.  
  1525. int kbd_ready()
  1526. {
  1527.   KBDKEYINFO ki;
  1528.  
  1529.   if (KbdPeek(&ki, 0)==0)
  1530.   {
  1531.     if (ki.fbStatus & 0x40) {
  1532.       int key = (USHORT)((ki.chScan << 8) | ki.chChar);
  1533.       myShift = ki.fsState;
  1534.       if (!key && ki.fbStatus & 0x01) {
  1535.         KbdCharIn(&ki, IO_WAIT, 0);
  1536.       }
  1537.       return key;
  1538.     }
  1539.   }
  1540.   return 0;
  1541. }
  1542.  
  1543. int kbd_getkey()
  1544. {
  1545.   KBDKEYINFO ki;
  1546.   
  1547.   while (KbdCharIn(&ki, IO_WAIT, 0)==0) {
  1548.     if (ki.fbStatus & 0x40) {
  1549.       myShift = ki.fsState;
  1550.       return (USHORT)((ki.chScan << 8) | ki.chChar);
  1551.     }
  1552.     if (ki.fbStatus & 0x01) {
  1553.       myShift = ki.fsState;
  1554.     }
  1555.   }
  1556.   
  1557.   return NODATA;
  1558. }
  1559.  
  1560. void kbd_flush()
  1561. {
  1562.   KbdFlushBuffer(0);
  1563. }
  1564.  
  1565. int kbd_shift()
  1566. {
  1567.   return myShift;
  1568. }
  1569.